package com.dz.risk.common.utils;

import com.dz.risk.exception.VerifyException;
import lombok.extern.slf4j.Slf4j;
import org.nfunk.jep.JEP;
import org.nfunk.jep.ParseException;
import org.nfunk.jep.function.If;
import org.nfunk.jep.function.PostfixMathCommand;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

/**
 * @author Doke
 * @date 2021/8/11 9:33
 */
@Slf4j
public class JepUtils {

    /**
     * 公式计算
     *
     * @param exp 公式详情
     * @return 计算结果
     */
    public static Double parseExpression(String exp, Map<String, Double> params) {
        JEP jep = new JEP();
        double result;

        // 添加常用函数
        jep.addStandardFunctions();
        jep.addFunction("MAX", new Max(2));
        jep.addFunction("MIN", new Min(2));
        jep.addFunction("IF",new If());

        // 添加常用常量
        jep.addStandardConstants();

        Set<String> key = params.keySet();

        for (String k : key) {
            exp = exp.replace(k,String.valueOf(params.get(k)));
        }

        try {
            jep.parseExpression(exp);
            result = jep.getValue();
        } catch (Throwable e) {
            log.error("An error occured: " + e.getMessage());
            throw new VerifyException(exp + " 计算出错,请检查");
        }

        //保留两位小数第三位如果大于4会进一位（四舍五入）
        return new BigDecimal(result).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 自定义取两个参数之间最小值函数
     */
    static class Min extends PostfixMathCommand {

        private int paramNum;

        public Min(int paramNum) {
            super();
            numberOfParameters = paramNum;
        }

        @Override
        public void run(Stack inStack) throws ParseException {
            //检查栈
            checkStack(inStack);
            Object param2 = inStack.pop();
            Object param1 = inStack.pop();

            if ((param1 instanceof Number) && (param2 instanceof Number)) {
                double p1 = ((Number) param2).doubleValue();
                double p2 = ((Number) param1).doubleValue();

                double result = Math.min(p1, p2);

                inStack.push(result);
            } else {
                throw new ParseException("Invalid parameter type");
            }
        }

    }

    /**
     * 自定义取两个参数之间最大值函数
     */
    static class Max extends PostfixMathCommand {

        private int paramNum;

        public Max(int paramNum) {
            super();
            numberOfParameters = paramNum;
        }

        @Override
        public void run(Stack inStack) throws ParseException {
            //检查栈
            checkStack(inStack);
            Object param2 = inStack.pop();
            Object param1 = inStack.pop();

            if ((param1 instanceof Number) && (param2 instanceof Number)) {
                double p1 = ((Number) param2).doubleValue();
                double p2 = ((Number) param1).doubleValue();

                double result = Math.max(p1, p2);

                inStack.push(result);
            } else {
                throw new ParseException("Invalid parameter type");
            }
        }

    }
}
